﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using SpiritEngine.Source.Utilities;


namespace SpiritEngine.Source.ApplicationLayer
{
    #region Globals

    // Spirit Engine object flags.
    // NOTE: This enum is placed here so we don't have to qualify it with the class name to use it. For example: SE_Base.SEObject_Flags
    public enum SEObject_Flags
    {
        Initialized = 1,
        Disposed = 2,
    }

    #endregion

    /// <summary>
    /// This class is the root of nearly all other classes within the SpiritEngine.
    /// </summary>
#if USE_IDISPOSABLE
    public class SE_Base: ISE_Object, IDisposable
#else
    public class SE_Base
#endif
    {
        #region Constants/Enums

        // NOTE: Enums are a type, not a variable.  Therefore enum's are static by definition.  This means that we don't have to worry about
        //       memory being wasted by having a separate copy of the enum for each instance of the class.



        #endregion


        #region Structs

        #endregion


        #region Static Member Variables and Methods

        #endregion


        #region Member Variables

        // ========================================================================================================================================================================================================
        // Property Variables
        // ========================================================================================================================================================================================================

        protected object m_Parent = null;


        // These are private to force classes that inherit from this class to declare their own member variable for use by the corresponding properties.
        // This way, for example, we can keep track of initialization state for each step in the inheritence chain.  So if the base class is initialized, but
        // the subclass is not, then the base class's IsInitialized property would return true, but the subclass's IsInitialized property would return false.
        private int m_SEObjectFlags = 0;    // Stores object flags defined by the SEObject_Flags enum.

        #endregion


        #region Events/Delegates

        #endregion


        #region Constructors and Destructors (aka Finalizers)

        #endregion


        #region Non-Public Methods

        // This section is for methods that are declared as private, protected, protected internal, or internal.

        #endregion


        #region Public Methods

        /// <summary>
        /// This function initializes the object.  Why not use the constructor?  The anwser is because we don't want to ever
        /// put code that could fail into a constructor.  If that code fails, we could have an object that is only partially
        /// initialized, and therefore invalid.
        /// 
        /// EXPECTED PARAMETERS:
        /// None</param>
        /// <param name="paramsList">A variable length parameter list.</param>
        /// <returns>True if successful, or false otherwise.</returns>
        public virtual bool Initialize(params object[] paramsList)
        {
            // base.Initialize();

#if VERIFY_PARAMS
            if (!SE_Debug.VerifyParameters(paramsList))
            {
                throw new ArgumentException("SE_Base.Initialize(object[]): Invalid params!");
            }
#endif


            // Set the initialized flag to indicate that this object has been initialized.
            m_SEObjectFlags = SE_Misc.Flags_SetFlags(m_SEObjectFlags, (int) SEObject_Flags.Initialized);

            return true;
        }

        #endregion


        #region Interface Methods

        // This section is for methods that are part of the interfaces that the class implements.

        #region IDispose Members

#if USE_IDISPOSABLE

        /// <summary>
        /// This public method is called to dispose of this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);

            this.SEObjectFlags = SE_Misc.Flags_SetFlags(this.SEObjectFlags, (int) SEObject_Flags.Disposed);
            this.SEObjectFlags = SE_Misc.Flags_ClearFlags(this.SEObjectFlags, (int) SEObject_Flags.Initialized);

        }


        /// <summary>
        /// This function handles the actual "disposing" of this object.
        /// ANY classes that are inherited from this class, no matter how distantly, MUST override this method so that they can perform their own cleanup!
        /// </summary>
        /// <param name="disposing">This parameter will be true if this function is being called by the public Dispose() method, or false otherwise.</param>
        protected virtual void Dispose(bool disposing)
        {
            // Check if this object has already been disposed.
            if (SE_Misc.Flags_HasFlags(m_SEObjectFlags, (int) SEObject_Flags.Disposed))
                return;



            if (disposing)
            {
                /*
                * The following text is from MSDN  (http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx)
                * 
                * 
                * Dispose(bool disposing) executes in two distinct scenarios:
                * 
                * If disposing equals true, the method has been called directly or indirectly by a user's code and managed and unmanaged resources can be disposed.
                * If disposing equals false, the method has been called by the runtime from inside the finalizer and only unmanaged resources can be disposed. 
                * 
                * When an object is executing its finalization code, it should not reference other objects, because finalizers do not execute in any particular order. 
                * If an executing finalizer references another object that has already been finalized, the executing finalizer will fail.
                */


                // Unregister Events
                

                // Clean up all managed resources.
                m_Parent = null;

            }

            // Clean up all unmanaged resources.


            // Call the base class's Dispose() method if it has one.
            // base.Dispose(disposing);

        }

#endif

        #endregion

        #endregion


        #region Event Invocation Methods

        // This section is for methods that invoke the events of this class.  For example:
        //
        // Invoke the Changed event; called whenever list changes
        // protected virtual void OnChanged(EventArgs e) 
        // {
        //		if (Changed != null)
        //			Changed(this, e);
        // }

        #endregion


        #region Operators

        #endregion


        #region Debug Methods

        // This will compile the debug code only if the DEBUG symbol is defined.
        // To define this symbol for all source files in the project, open the Project Properties,
        // and go to the Build tab.  Check the "Define DEBUG constant" checkbox.  With this box checked,
        // the compiler will automatically define the symbol DEBUG.
#if (DEBUG)
        // put all debug methods in here...
#endif

        #endregion


        #region Properties

        /// <summary>
        /// Returns true if this object has been disposed or false otherwise.
        /// </summary>
        public bool IsDisposed
        {
            get
            {
                // This uses the SEObjectFlags property rather than directly accessing the member variable m_SEObjectFlags
                // because that way it will always use the most derived version of that property, and thus
                // access the flags of the derived class.  This is necessary because otherwise, when these
                // properties are called on a subclass, the base class property value would be returned
                // instead of the subclass's property value.
                return SE_Misc.Flags_HasFlags(this.SEObjectFlags, (int) SEObject_Flags.Disposed);
            }
        }

        /// <summary>
        /// Returns true if this object has been initialized, or false otherwise.
        /// </summary>
        public bool IsInitialized
        {
            get
            {
                // This uses the SEObjectFlags property rather than directly accessing the member variable m_SEObjectFlags
                // because that way it will always use the most derived version of that property, and thus
                // access the flags of the derived class.  This is necessary because otherwise, when these
                // properties are called on a subclass, the base class property value would be returned
                // instead of the subclass's property value.
                return SE_Misc.Flags_HasFlags(this.SEObjectFlags, (int) SEObject_Flags.Initialized);
            }
        }

        /// <summary>
        /// Returns a reference to the parent of this object, or null if there is no parent.
        /// 
        /// NOTE: When casting the result of this property, use the AS keyword instead of a normal
        /// parentheses cast.  For example, do this:
        /// var newVar = objName.Parent as NewType
        /// But don't do this:
        /// var newVar = (NewType) objName.Parent;
        /// The reason for this is because AS is faster than the normal parentheses type cast.
        /// For more info, see this article on the subject: 
        /// http://www.codeproject.com/Articles/8052/Type-casting-impact-over-execution-performance-in
        /// </summary>
        public object Parent
        {
            get
            {
                return m_Parent;
            }
        }

        /// <summary>
        /// Provides access to the object's "Spirit Engine Object" flags.
        /// </summary>
        public virtual int SEObjectFlags
        {
            get
            {
                return m_SEObjectFlags;
            }
            protected set // Can only be set from within this class
            {
                m_SEObjectFlags = value;
            }
        } 

        #endregion

    }
}
